//
//  ThreadedServer.m
//  SchmoozingExamples
//
//  Created by garrison on Fri Apr 20 2001.
//  Copyright (c) 2001 Standard Orbit Software, LLC. All rights reserved.
//
//  Permission is granted to use this code for any purpose, at your own risk.
//  No warranties are expressed or implied.
//

/* An Algorithm for a Concurrent Server [Comer93]

Master 1) Create a socket for the server and bind it to the well known port 
          for the service being offered.
Master 2) Establishing a listener on the socket.
Master 3) Repeatedly accept incoming requests and create new slaves to handle the response.
Slave 1) Receive a connection request/socket upon creation.
Slave 2) Interact with the client using the socket, reading requests and sending back replies.
Slave 3) Close the connection and exit.  The slave exits after handling all requests 
         from one client.
*/

#import "ThreadedServer.h"

#import <OmniNetworking/OmniNetworking.h>
#import "Connection.h"

@implementation ThreadedServer

- (id) initWithLocalPort:(unsigned short) aPort
{
    self = [super init];
    
    if ( self )
    {
        NS_DURING {
            
            listeningSocket = [[ONTCPSocket tcpSocket] retain];
            // Master Step 1. Create an ONTCPSocket for the server.
            
            [listeningSocket startListeningOnLocalPort:aPort allowingAddressReuse:YES];
            // Master Step 2.
            // Start socket listening on the well known port.  Allowing address reuse is
            // helpful while debugging (it prevents a port from getting locked up if your
            // app crashes without closing the socket).
            
            NSLog(@"Listening for connections on host %@:%d", [ONHost localHostname], aPort);
        }
        NS_HANDLER {
            // Handle potential exceptions.
            NSLog(@"%@ raised while starting the server: %@", 
                [localException name], localException);
            [listeningSocket release];
            return nil;
        }
        NS_ENDHANDLER
    }
        
    return self;
}


- (void) dealloc
{
    [listeningSocket release];
    [super dealloc];
}


- (void) run
{
    // This method implements a concurrent server using threads.  A new client
    // connection object is created for each accepted connection.  A new
    // thread is detached to then run the client connection's processing method.
    
    ONTCPSocket *connectionSocket = nil;
    Connection *someClient = nil;
    unsigned short connectionCount = 0;
    
    // 'connectionSocket' will hold the accepted connection, leaving 
    // the listening socket free to continue listening.
    // 'someClient' is an object of our own custom class for handling
    // connections to our server.
    // 'connectionCount' holds a count of connections accepted, just to
    // provide some feedback in the spartan UI.
 
    NSLog(@"Threaded Concurrent Server");
    
    do 
    {
        NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];

        NS_DURING 
        {            
            NSLog(@"Main thread listening for next connection");
            
            connectionSocket = [listeningSocket acceptConnectionOnNewSocket];
            // Master Step 3.  Accept the next incoming connection.  The main
            // thread will block until a connection request is received.
            
            connectionCount += 1;
            // Increment the accepted connection count

            NSLog(@"Accepted connection %d from host %@", connectionCount, 
                    [[connectionSocket remoteAddressHost] hostname]);
            
            someClient = [[Connection alloc] initWithConnectedSocket:connectionSocket];
            // Slave Step 1.  Receive the accepted connection when our slave is
            // initialized.  The role of Slave is played by 'someClient'.
            
            NSLog(@"Detaching a thread to process the connection");
            [NSThread detachNewThreadSelector:@selector(processConnection)  
                                toTarget:someClient  withObject:nil];
            // Slave Step 2.  Spawn off a new thread to run our server's interaction
            // with the client.
            
            [loopPool release];
            // Since detachNewThreadSelector: will retain our Connection object, 
            // (and Connection retained the accepted socket object on init), we can
            // safely release the loop pool (thereby removing our responsibility 
            // for these objects).  When the thread terminates, those objects
            // will be released (and the socket will be closed).
        }
        
        NS_HANDLER 
        {
            NSLog(@"%@ raised during threaded server processing: %@", 
                    [localException name], localException);
            [loopPool release];
            break;
            // Close down the server loop if an error occurs.
        }
        NS_ENDHANDLER

    } while (1);
    // Loop forever listening for and accepting connections.  As implemented here, the only way 
    // the server will quit is if an exception is raised during execution.  You will want to add
    // some additional means of gracefully bringing a server down (e.g. handling
    // a SIGTERM or SIGHUP signal).
}
@end
